package com.zhaowenhao.hello.spring.framework.aop.support;

import com.zhaowenhao.hello.spring.framework.aop.aspect.WHAdvice;
import com.zhaowenhao.hello.spring.framework.aop.config.WHAopConfig;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 主要的工作：完成切面类配置信息到advice列表的转变，此处为了简单考虑用Map来代替list
 *
 * @author zwh
 */
public class WHAdvisedSupport {

    private Class<?> targetClass;

    private Object target;

    private WHAopConfig config;

    private Pattern pointCutClassPattern;

    private transient Map<Method, Map<String, WHAdvice>> methodCache;

    public WHAdvisedSupport(WHAopConfig config) {
        this.config = config;
    }


    public Class<?> getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    private void parse() {
        String pointCut = this.config.pointCut
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");
        // pointCut=public .* com.gupaoedu.vip.demo.service..*Service..*(.*)
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        //提取class的全名
        pointCutClassPattern = Pattern.compile(pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));

        try {
            //开始映射目标类方法和通知的关系
            methodCache = new HashMap<Method, Map<String, WHAdvice>>();

            //开始匹配目标类的方法
            Pattern pointCutPattern = Pattern.compile(pointCut);

            //先把要织入的切面的方法缓存起来
            Class aspectClass = Class.forName(this.config.getAspectClass());
            Map<String, Method> aspectMethods = new HashMap<String, Method>();
            for (Method method : aspectClass.getMethods()) {
                aspectMethods.put(method.getName(), method);
            }

            //扫描目标类的所有的方法
            for (Method method : this.targetClass.getMethods()) {
                //包括了修饰符、返回值、方法名、形参列表
                String methodString = method.toString();
                //把异常去掉
                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }

                Matcher matcher = pointCutPattern.matcher(methodString);
                if (matcher.matches()) {
                    Map<String, WHAdvice> advices = new HashMap<String, WHAdvice>();

                    //前置通知
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        advices.put("before",
                                new WHAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore())));
                    }

                    //后置通知
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        advices.put("after",
                                new WHAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter())));
                    }

                    //异常通知
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        advices.put("afterThrowing",
                                new WHAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfterThrow())));
                    }

                    //一个原始method 对应一个Map,Map的映射关系就是通知类型对应的增强方法
                    methodCache.put(method, advices);
                }

            }


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public WHAopConfig getConfig() {
        return config;
    }

    public void setConfig(WHAopConfig config) {
        this.config = config;
    }

    public Pattern getPointCutClassPattern() {
        return pointCutClassPattern;
    }

    public void setPointCutClassPattern(Pattern pointCutClassPattern) {
        this.pointCutClassPattern = pointCutClassPattern;
    }

    public Map<Method, Map<String, WHAdvice>> getMethodCache() {
        return methodCache;
    }

    public void setMethodCache(Map<Method, Map<String, WHAdvice>> methodCache) {
        this.methodCache = methodCache;
    }

    public boolean pointCutMatch() {
        return pointCutClassPattern.matcher(targetClass.getName()).matches();
    }

    public Map<String, WHAdvice> getAdvices(Method method, Class<?> targetClass) throws NoSuchMethodException {
        Map<String, WHAdvice> adviceMap = methodCache.get(method);
        if (adviceMap == null) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            adviceMap = methodCache.get(m);
            methodCache.put(method, adviceMap);
        }
        return adviceMap;
    }
}
